/*
 * Copyright (C) 2007-2009 Alessandro Melandri
 * 
 * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or any later version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

package net.melandri.jtextfileparser.beans;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;

import net.melandri.jtextfileparser.dao.FileManager;
import net.melandri.jtextfileparser.filters.OutputFileFilter;
import net.melandri.jtextfileparser.utils.JTextFileParserException;

/**
 * With this class you can parse and manage delimited textfiles.
 * 
 * <p><strong>Usage</strong></p>
 * 
 * <pre>
 * TextFile myFile = new TextFile("C:\\temp\\","miFile.csv", "\t");
 * </pre>
 * 
 * @author Alessandro Melandri
 */
public class TextFile {

	private String fileName;
	private String filePath;
	private ArrayList rows;
	private Row headerRow;
	
	private FileManager fileManager;
	
	private final String SEP;
	private String textFieldDelimiter;
	private boolean hasHeader = false;


	/**
	 * Builds a TextFile object from the specified text file.
	 * 
	 * @param filename Name of the file
	 * @param filePath Path where the file is stored.
	 * @param sep Separator beetween fields.
	 */
	public TextFile(String fileName, String filePath, String sep) throws FileNotFoundException {
		
		this.fileName = fileName;
		this.filePath = filePath;
		this.SEP = sep;
		textFieldDelimiter = "";
		
		fileManager = new FileManager(this.fileName, this.filePath);
		
		this.rows = parse(1);
	}


	/**
	 * Builds a TextFile object from the specified text file.
	 * 
	 * @param filename Name of the file
	 * @param filePath Path where the file is stored.
	 * @param sep Separator beetween fields.
	 * @param hasHeader Specifies if the first line is a header and should be ignored
	 */
	public TextFile(String fileName, String filePath, String sep, boolean hasHeader) throws FileNotFoundException {
		
		this.hasHeader = hasHeader;
		
		this.fileName = fileName;
		this.filePath = filePath;
		this.SEP = sep;
		textFieldDelimiter = "";
		
		fileManager = new FileManager(this.fileName, this.filePath);
		
		this.rows = parse(0);

		if (hasHeader) {
			this.headerRow = (Row) this.rows.get(0);
			this.rows.remove(0);
		}
	}


	/**
	 * Builds a TextFile from an ArrayList of Rows objects
	 * 
	 * @param rows ArrayList of Row objects
	 * @param sep Fields separator
	 * @since 1.3
	 */
	public TextFile(ArrayList rows, String sep) {
		
		this.rows = rows;
		this.SEP = sep;
	}


	/**
	 * Returns the specified row as a String
	 * 
	 * @param rowNumber number of the row in the file
	 * @return Returns a String with the original row
	 */
	public String getStringRow(int rowNumber) throws JTextFileParserException {

		Row thisRow = null;

		try {
			thisRow = (Row) rows.get(rowNumber);
		} catch (IndexOutOfBoundsException e) {
			e.printStackTrace();
			throw new JTextFileParserException("You've specifed a row ID greater than the total row number");
		}

		return thisRow.getStringRow();
	}


	/**
	 * Returns the specified row as a Row object
	 * 
	 * @param ronNumber number of the row in the file
	 * @return Returns the corresponding Row object
	 */
	public Row getRow(int rowNumber) {
		return (Row) rows.get(rowNumber);
	}
	
	
	/**
	 * Removes a row from text file
	 * 
	 * @param rowIndex Row index to be removed
	 */
	public void removeRow(int rowIndex){
		
		rows.remove(rowIndex);
		
	}


	/**
	 * Returns all text file rows
	 * @return Returns an ArrayList of Row objects
	 */
	public ArrayList getRows() {
		return rows;
	}


	/**
	 * Replaces all rows int the text file.
	 * 
	 * @param rows ArrayList of rows objects
	 * @since 1.2
	 */
	public void replaceRows(ArrayList rows) {
		this.rows = rows;
	}


	/**
	 * Returns the number of rows in the file
	 * 
	 * @return Returns the number of rows in the file
	 */
	public int lenght() {
		return rows.size();
	}


	/**
	 * Checks if all the rows have the same number of fields
	 * 
	 * @return Returns true if all the the rows have the same number of fields, otherwise it returns false
	 */
	public boolean checkRowsLength() {

		boolean check = true;

		Iterator it = rows.iterator();
		int sizeHelper = 0;

		while (it.hasNext() && check) {

			Row thisRow = (Row) it.next();
			int size = thisRow.getFields().length;

			if (sizeHelper == 0)
				sizeHelper = size;
			else if (size != sizeHelper)
				check = false;
		}

		return check;
	}


	/**
	 * Checks if all the rows have the specified number of fields
	 * 
	 * @param fieldsNumber The number of expected fields in a row.
	 * @return Returns an ArrayList of rows that don't have the specified number of fields
	 */
	public ArrayList checkRowsLength(int fieldsNumber) {

		ArrayList wrongRows = new ArrayList();

		Iterator it = rows.iterator();

		while (it.hasNext()) {

			Row thisRow = (Row) it.next();

			if (thisRow.getFields().length != fieldsNumber)
				wrongRows.add(thisRow);
		}

		return wrongRows;
	}


	/**
	 * Checks if the specified field has the same length in every row.
	 * 
	 * @param fieldPosition The field to check (numeration starts from 0)
	 * @param fieldLength Expected field length
	 * 
	 * @return Returns an ArraList of Rows.
	 */
	public ArrayList checkField(int fieldPosition, int fieldLength) {

		ArrayList wrongRows = new ArrayList();

		Iterator it = rows.iterator();

		while (it.hasNext()) {

			Row thisRow = (Row) it.next();
			String field = (String) thisRow.getFields()[fieldPosition];

			if (field.length() != fieldLength)
				wrongRows.add(thisRow);
		}

		return wrongRows;
	}


	/**
	 * Creates a new file based on the OutputFileFilter provided
	 * 
	 * @param filter Filter implementing the OutputFileFilter interface and extending the abstract class OutputFileFilterImpl
	 * @since 1.4
	 * */
	public void write(OutputFileFilter filter){
		filter.setTextFieldDelimiter(this.textFieldDelimiter);
		filter.write(this.rows);
	}
	

	/**
	 * @return The name of the file
	 */
	public String getFileName() {
		return fileName;
	}


	/**
	 * @return Returns the header row
	 */
	public Row getHeaders() {
		return headerRow;
	}


	/**
	 * Checks if the TextFile has an header row
	 * 
	 * @since 1.3
	 */
	public boolean hasHeader() {
		return hasHeader;
	}
	
	
	/**
	 * Sets the text field delimiter
	 * @param textFieldDelimiter Text Field Delimiter
	 */
	public void setTextFieldDelimiter(String textFieldDelimiter){
		this.textFieldDelimiter = textFieldDelimiter;
	}
	
	
	/**
	 * Parses the file and returns an ArrayList of Row objects
	 * 
	 * @param numStart   Starting line for parsing
	 * @return 		  Retuns an ArrayList of Row objects
	 * 
	 * @since 1.3.1
	 */
	private ArrayList parse(int numStart) {

		ArrayList file = new ArrayList();
		String textRow = null;

		int rowCounter = numStart;

		try {
			
			fileManager.loadFile();

			while ((textRow = fileManager.readLine()) != null) {

				if (!textRow.trim().equals("")) {
					Row thisRow = new Row(textRow, SEP, rowCounter);
					file.add(thisRow);
				}
				rowCounter++;
			}

		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				fileManager.closeRead();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

		return file;
	}
}